package com.cnct.generic.dubbo.handler;


import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.cnct.generic.dubbo.holder.ReferenceHolder;
import com.cnct.generic.dubbo.holder.RegistryHolder;
import com.cnct.generic.dubbo.properties.GenericDubboProperties;
import com.cnct.generic.dubbo.strategy.RequireReferenceStrategy;
import com.cnct.generic.dubbo.utils.KeyGenerateUtil;
import com.cnct.generic.dubbo.utils.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Dubbo核心执行器
 *
 * @author wangzhen
 */
@Slf4j
@Component
public class DubboInvokeCoreHandler implements InvokeCoreHandler, InitializingBean {


    public static ApplicationConfig application = new ApplicationConfig();

    private ReferenceHolder referenceHolder;

    private RegistryHolder registryHolder;

    private GenericDubboProperties properties;

    @Autowired
    private Map<String, RequireReferenceStrategy> strategy = new HashMap<>();


    public DubboInvokeCoreHandler(ReferenceHolder referenceHolder,
                                  RegistryHolder registryHolder,
                                  GenericDubboProperties properties) {
        this.referenceHolder = referenceHolder;
        this.registryHolder = registryHolder;
        this.properties = properties;
    }


    @Override
    public Object invoke(String interfaceName, String methodName, List<Object> paramList) {
        return this.invoke(interfaceName, methodName, null, null, paramList, properties.getRegistryAddress(), null);
    }

    @Override
    public Object invoke(String interfaceName, String methodName, List<Object> paramList, String group) {
        return this.invoke(interfaceName, methodName, null, group, paramList, properties.getRegistryAddress(), null);
    }

    @Override
    public Object invoke(String interfaceName, String methodName, String url, List<Object> paramList) {
        return this.invoke(interfaceName, methodName, url, null, paramList, properties.getRegistryAddress(), null);
    }

    @Override
    public Object invoke(String interfaceName, String methodName, String url, List<Object> paramList, String group) {
        return this.invoke(interfaceName, methodName, url, group, paramList, properties.getRegistryAddress(), null);
    }


    /**
     * 核心执行方法
     *
     * @param interfaceName 接口名称 {全名如 com.test.service.HelloService }
     * @param methodName    方法名称 方法名称 {如: sayHello }
     * @param url           直连地址
     * @param group         分组
     * @param paramList     参数List集合
     * @param address       注册中心地址
     * @param version       服务提供版本号
     * @return
     */
    @Override
    public Object invoke(String interfaceName, String methodName, String url, String group, List<Object> paramList, String address, String version) {

        String choose = StringUtils.isNotEmpty(url) ? ReferenceHolder.DIRECT : ReferenceHolder.REGISTRY;

        ReferenceConfig reference = strategy.get(choose + RequireReferenceStrategy.class.getSimpleName()).requireReferenceConfig(application, registryHolder.requireRegistryConfig(address), group, url, interfaceName, version);

        if (null != reference) {

            GenericService genericService = (GenericService) reference.get();
            if (genericService == null) {
                log.debug("GenericService 不存在:{}", interfaceName);
                registryHolder.removeRegistryConfig(address);
                referenceHolder.removeReferenceConfig(KeyGenerateUtil.referenceKey(interfaceName, group, version));
                return null;
            }

            //方法参数
            Object[] paramObject = null;
            if (!CollectionUtils.isEmpty(paramList)) {
                paramObject = new Object[paramList.size()];
                for (int i = 0; i < paramList.size(); i++) {
                    paramObject[i] = paramList.get(i);
                }
            }

            Object resultParam = genericService.$invoke(methodName, ReflectUtil.getMethodParamType(interfaceName, methodName), paramObject);

            return resultParam;
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        application.setName(properties.getApplicationName());


    }
}